﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Web.Security;

namespace TireService.Data
{
    public class TaskBLL
    {
        /// <summary>
        /// Function that create a task
        /// </summary>
        /// <param name="db">Connection to database</param>
        /// <param name="TaskTypeId">Id on tasktype</param>
        /// <param name="TaskDetailTypeId">Id on taskdetailtype</param>
        /// <param name="TaskTypeStatusId">Id on tasktype status</param>
        /// <param name="CustomerId">Id on customer to create task for</param>
        /// <param name="CarId">Id on car related to task</param>
        /// <param name="TireSetId">Id on tireset related to task (if tirechange, this is the tireset the when task is ended the tireset will be put on the car</param>
        /// <param name="Address"></param>
        /// <param name="ZipCode"></param>
        /// <param name="City"></param>
        /// <param name="CountryId"></param>
        /// <param name="CommentToAddress"></param>
        /// <param name="SecondaryAddress"></param>
        /// <param name="SecondaryZipCode"></param>
        /// <param name="SecondaryCity"></param>
        /// <param name="SecondaryCountryId"></param>
        /// <param name="CommentToSecondaryAddress"></param>
        /// <param name="NoteToEmployee"></param>
        /// <param name="Amount"></param>
        /// <param name="UserId"></param>
        /// <param name="SubmitInTransaction">True/false value, True indicates that change is being submitted in transaction</param>
        public Task CreateTask(TireServiceDB db, int? DependentTaskId, int TaskTypeId, int TaskDetailTypeId, int TaskTypeStatusId, int CustomerId, int CarId, int TireSetId, string Address, string ZipCode, string City, int CountryId, string CommentToAddress, string SecondaryAddress, string SecondaryZipCode, string SecondaryCity, int? SecondaryCountryId, string CommentToSecondaryAddress, string NoteToEmployee, decimal Amount, Guid UserId, bool SubmitInTransaction)
        {           
            // Get car and tireset for validation
            Car car = db.GetCar(CarId);
            TireSet tireset = db.GetTireSet(TireSetId);

            // Initiate new task
            Task task = new Task();
            TaskDetail taskdetail = new TaskDetail();

            // Validation
            if (TaskTypeId == (int)Shared.TaskType.ServiceDrive)
            {
                // Apply validation for service drive tasks
                ValidateServiceDrive(db, TaskDetailTypeId, car, tireset, task);
            } 
            
            if (TaskTypeId == (int)Shared.TaskType.StockTask)
            {
                // Specific validation not added here yet
                ValidateStockTask(db, TaskDetailTypeId, tireset.TireSetId);
            }

            task.DependentTaskId = DependentTaskId;
            task.TaskTypeId = TaskTypeId;
            task.TaskTypeStatusId = TaskTypeStatusId;
            task.CustomerId = CustomerId;
            task.CarId = CarId;
            task.TireSetId = TireSetId;
            task.PrimaryAddress = Address;
            task.PrimaryZipCode = ZipCode;
            task.PrimaryCity = City;
            task.PrimaryCountryId = CountryId;
            task.CommentToAddress = CommentToAddress;

            // Calculate longitude and latitude for address
            try
            {
                string primaryAddress = Address + ", " + ZipCode + " " + City + ", " + db.GetCountryName(Convert.ToInt32(task.PrimaryCountryId));
                GoogleMaps googleMapAPI = new GoogleMaps(ConfigurationSettings.AppSettings["MapKey"]);
                double[] LongLatPrimary = googleMapAPI.GetLongLatCoordinates(primaryAddress);
                task.PrimaryLongitude = LongLatPrimary[0];
                task.PrimaryLatitude = LongLatPrimary[1];
            }
            catch (Exception)
            {
                // Error calculating longitude and latitude - values will be set to null
            }

            if (SecondaryAddress != null) task.SecondaryAddress = SecondaryAddress;
            if (SecondaryZipCode != null) task.SecondaryZipCode = SecondaryZipCode;
            if (SecondaryCity != null) task.SecondaryCity = SecondaryCity;
            if (SecondaryAddress != null & SecondaryZipCode != null & SecondaryCity != null)
            {
                // Calculate longitude and latitude for secondary address
                try
                {
                    string secondaryAddress = SecondaryAddress + ", " + SecondaryZipCode + " " + SecondaryCity + ", " + db.GetCountryName(Convert.ToInt32(task.PrimaryCountryId));
                    GoogleMaps googleMapAPI = new GoogleMaps(ConfigurationSettings.AppSettings["MapKey"]);
                    double[] LongLatSecondary = googleMapAPI.GetLongLatCoordinates(secondaryAddress);
                    task.SecondaryLongitude = LongLatSecondary[0];
                    task.SecondaryLatitude = LongLatSecondary[1];
                    task.SecondaryCountryId = CountryId;
                }
                catch (Exception)
                {
                    // Error calculating longitude and latitude - values will be set to null
                }

            }
            else
            {
                task.SecondaryLatitude = null;
                task.SecondaryLatitude = null;
            }
            task.CommentToSecondaryAddress = CommentToSecondaryAddress;
            task.UserId = UserId;

            // TODO: Consider generate invoice to later

            db.AddTask(task);

            // Update taskdetail
            AddTaskDetailToExistingTask(db, task, taskdetail, TaskDetailTypeId, Amount, NoteToEmployee, UserId, true);

            // Check if car is being created/updated in other transaction
            if (!SubmitInTransaction)
            {
                // Submit changes to database
                db.SubmitChanges();
            }

            return task;
        }

        public Task CreateTask(TireServiceDB db, int? DependentTaskId, int TaskTypeId, int TaskDetailTypeId, int TaskTypeStatusId, Customer Customer, Car Car, TireSet TireSet, string Address, string ZipCode, string City, int CountryId, string CommentToAddress, string SecondaryAddress, string SecondaryZipCode, string SecondaryCity, int? SecondaryCountryId, string CommentToSecondaryAddress, string NoteToEmployee, decimal Amount, Guid UserId, bool SubmitInTransaction)
        {
            // Initiate new task
            Task task = new Task();
            TaskDetail taskdetail = new TaskDetail();

            // Validation
            if (TaskTypeId == (int)Shared.TaskType.ServiceDrive)
            {
                // Apply validation for service drive tasks
                ValidateServiceDrive(db, TaskDetailTypeId, Car, TireSet, task);
            }

            if (TaskTypeId == (int)Shared.TaskType.StockTask)
            {
                // Specific validation not added here yet
                ValidateStockTask(db, TaskDetailTypeId, TireSet.TireSetId);
            }           

            task.DependentTaskId = DependentTaskId;
            task.TaskTypeId = TaskTypeId;
            task.TaskTypeStatusId = TaskTypeStatusId;
            Customer.Tasks.Add(task);
            Car.Tasks.Add(task);
            TireSet.Tasks.Add(task);
            task.PrimaryAddress = Address;
            task.PrimaryZipCode = ZipCode;
            task.PrimaryCity = City;
            task.PrimaryCountryId = CountryId;
            task.CommentToAddress = CommentToAddress;

            // Calculate longitude and latitude for address
            try
            {
                string primaryAddress = Address + ", " + ZipCode + " " + City + ", " + db.GetCountryName(Convert.ToInt32(task.PrimaryCountryId));
                GoogleMaps googleMapAPI = new GoogleMaps(ConfigurationSettings.AppSettings["MapKey"]);
                double[] LongLatPrimary = googleMapAPI.GetLongLatCoordinates(primaryAddress);
                task.PrimaryLongitude = LongLatPrimary[0];
                task.PrimaryLatitude = LongLatPrimary[1];
            }
            catch (Exception)
            {
                // Error calculating longitude and latitude - values will be set to null
            }

            if (SecondaryAddress != null) task.SecondaryAddress = SecondaryAddress;
            if (SecondaryZipCode != null) task.SecondaryZipCode = SecondaryZipCode;
            if (SecondaryCity != null) task.SecondaryCity = SecondaryCity;
            if (SecondaryAddress != null & SecondaryZipCode != null & SecondaryCity != null)
            {
                // Calculate longitude and latitude for secondary address
                try
                {
                    string secondaryAddress = SecondaryAddress + ", " + SecondaryZipCode + " " + SecondaryCity + ", " + db.GetCountryName(Convert.ToInt32(task.PrimaryCountryId));
                    GoogleMaps googleMapAPI = new GoogleMaps(ConfigurationSettings.AppSettings["MapKey"]);
                    double[] LongLatSecondary = googleMapAPI.GetLongLatCoordinates(secondaryAddress);
                    task.SecondaryLongitude = LongLatSecondary[0];
                    task.SecondaryLatitude = LongLatSecondary[1];
                    task.SecondaryCountryId = CountryId;
                }
                catch (Exception)
                {
                    // Error calculating longitude and latitude - values will be set to null
                }

            }
            else
            {
                task.SecondaryLatitude = null;
                task.SecondaryLatitude = null;
            }
            task.CommentToSecondaryAddress = CommentToSecondaryAddress;
            task.UserId = UserId;

            // TODO: Consider generate invoice to later

            db.AddTask(task);

            // Update taskdetail
            AddTaskDetailToExistingTask(db, task, taskdetail, TaskDetailTypeId, Amount, NoteToEmployee, UserId, true);

            // Check if car is being created/updated in other transaction
            if (!SubmitInTransaction)
            {
                // Submit changes to database
                db.SubmitChanges();
            }

            return task;
        }

        private void ValidateServiceDrive(TireServiceDB db, int TaskDetailTypeId, Car car, TireSet tireset, Task task)
        {
            if (TaskDetailTypeId == (int)Shared.TaskDetailType.ServiceDrive_PickUpTire)
            {
                // Validate on car status
                if (car.CarStatusId == (int)Shared.CarStatus.InActive)
                {
                    throw new ArgumentException("Afhentning af hjul kan ikke oprettes, da bilen er deaktiveret.");
                }

                // Validate on tireset status
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.MountedOnCar)
                {
                    throw new ArgumentException("Afhentning af hjul kan ikke oprettes, da dæk står til at være monteret på kundens bil.");
                }
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.AwaitsMoveToStock)
                {
                    throw new ArgumentException("Afhentning af hjul kan ikke oprettes, da dæk står til afventende lagermelding.");
                }
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.OnStock)
                {
                    throw new ArgumentException("Afhentning af hjul kan ikke oprettes, da dæk allerede står på lager.");
                }
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.ReturnedToCustomer)
                {
                    throw new ArgumentException("Afhentning af hjul kan ikke oprettes, da dæk er returneret til kunden.");
                }
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.InActive)
                {
                    throw new ArgumentException("Afhentning af hjul kan ikke oprettes, da dæk er deaktiveret / kasseret.");
                }

                // Check if dependency should be added to other task
                int[] tasktypestatus = new int[] { (int)Shared.TaskTypeStatus.ServiceDrive_Created, (int)Shared.TaskTypeStatus.ServiceDrive_Planned, (int)Shared.TaskTypeStatus.ServiceDrive_Replan };
                CheckAndCreateTaskDependency(db, car.CarId, tireset.TireSetId, tasktypestatus, task, TaskDetailTypeId);
            }

            if (TaskDetailTypeId == (int)Shared.TaskDetailType.ServiceDrive_ChangeTire)
            {
                // Validate on car status
                if (car.CarStatusId == (int)Shared.CarStatus.Active)
                {
                    throw new ArgumentException("Hjulskift kan ikke oprettes, da bilen står som aktiv.");
                }
                if (car.CarStatusId == (int)Shared.CarStatus.AwaitsTireChangePlanning)
                {
                    throw new ArgumentException("Hjulskift kan ikke oprettes, da der allerede eksistere åben opgave på dette.");
                }
                if (car.CarStatusId == (int)Shared.CarStatus.PlannedTireChange)
                {
                    throw new ArgumentException("Hjulskift kan ikke oprettes, da der allerede eksistere åben opgave på dette.");
                }
                // NOTE: Imidlertidigt udkommenteret pga. hjulskift skulle oprettes i systemet (1/3-2013)
                //if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.AwaitsMoveToStock)
                //{
                //    throw new ArgumentException("Hjulskift kan ikke oprettes, da dæk står til afventende lagermelding.");
                //}
                //if (car.CarStatusId == (int)Shared.CarStatus.TireChanged)
                //{
                //    throw new ArgumentException("Hjulskift kan ikke oprettes, da bilen har fået skiftet hjul.");
                //}
                if (car.CarStatusId == (int)Shared.CarStatus.InActive)
                {
                    throw new ArgumentException("Hjulskift kan ikke oprettes, da bilen er deaktiveret.");
                }

                // Validate on tireset status
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.MountedOnCar)
                {
                    throw new ArgumentException("Hjulskift kan ikke oprettes, da dæk står til allerede at være monteret på kundens bil.");
                }
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.ReturnedToCustomer)
                {
                    throw new ArgumentException("Hjulskift kan ikke oprettes, da dæk er returneret til kunden.");
                }
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.InActive)
                {
                    throw new ArgumentException("Hjulskift kan ikke oprettes, da dæk er deaktiveret / kasseret.");
                }

                // Check if dependency should be added to other task
                int[] tasktypestatus = new int[] { (int)Shared.TaskTypeStatus.ServiceDrive_Created, (int)Shared.TaskTypeStatus.ServiceDrive_Planned, (int)Shared.TaskTypeStatus.ServiceDrive_Replan };
                CheckAndCreateTaskDependency(db, car.CarId, tireset.TireSetId, tasktypestatus, task, TaskDetailTypeId);

                // Update status on car based on new task
                car.CarStatusId = (int)Shared.CarStatus.AwaitsTireChangePlanning;
            }

            if (TaskDetailTypeId == (int)Shared.TaskDetailType.ServiceDrive_ReturnTire)
            {
                // No need for car status validation - all scenarios are ok

                // Validate on tireset status
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.PlacedAtCustomer)
                {
                    throw new ArgumentException("Aflevering af hjul kan ikke oprettes, da dæk står hos kunden.");
                }
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.MountedOnCar)
                {
                    throw new ArgumentException("Aflevering af hjul kan ikke oprettes, da dæk er monteret på kundens bil.");
                }
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.StockedAtCustomer)
                {
                    throw new ArgumentException("Aflevering af hjul kan ikke oprettes, da dæk opbevares af kunden selv.");
                }
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.ReturnedToCustomer)
                {
                    throw new ArgumentException("Aflevering af hjul kan ikke oprettes, da dæk allerede er returneret til kunden.");
                }
                if (tireset.TireSetStatusId == (int)Shared.TireSetStatus.InActive)
                {
                    // Note til mig selv - denne validering kan måske blive irrelevant hvis vi ønsker at køre ud med deaktiveret / kasseret hjul til kunden
                    throw new ArgumentException("Aflevering af hjul kan ikke oprettes, da dæk er deaktiveret / kasseret.");
                }

                // No dependency check needed for this TaskDetailTypeId
            }
        }

        private void ValidateStockTask(TireServiceDB db, int TaskDetailTypeId, int TireSetId)
        {
            int[] tasktypestatus = new int[] { (int)Shared.TaskTypeStatus.StockTask_Created };
            if (db.CountTireSetActiveTaskDetails(TireSetId, TaskDetailTypeId, tasktypestatus) > 0)
            {
                throw new ArgumentException("Lignende opgave, som endnu ikke er afsluttet, er allerede oprettet.");
            }
        }

        public void AddTaskDetailToExistingTask(TireServiceDB db, Task task, TaskDetail taskdetail, int TaskDetailTypeId, decimal Amount, string NoteToEmployee, Guid UserId, bool SubmitInTransaction)
        {
            // TODO: Implementer eventuel validering ifht. hvis flere opgaver tilføjes - validering om de kan kobles sammen - ex. skal være samme detailtypeid og evt. andre ting

            task.TaskDetails.Add(taskdetail);
            taskdetail.TaskDetailTypeId = TaskDetailTypeId;
            taskdetail.Amount = Amount;
            taskdetail.NoteToEmployee = NoteToEmployee;

            db.AddTaskDetail(taskdetail);

            // Check if a new task is being created or if it already exists
            if (task.TaskId != 0 & task.TaskId != null)
            {
                // Create tasklog
                CreateTaskLog(db, task, DateTime.Now, null, task.TaskTypeStatusId, UserId, "Delopgave tilføjet til opgave");
            }
            else
            {
                // Create tasklog
                CreateTaskLog(db, task, DateTime.Now, null, task.TaskTypeStatusId, UserId, "Opgave oprettet");
            }

            // Check if car is being created/updated in other transaction
            if (!SubmitInTransaction)
            {
                // Submit changes to database
                db.SubmitChanges();
            }
        }

        /// <summary>
        /// Function that add dependency to a given task
        /// </summary>
        /// <param name="db">Connection to database</param>
        /// <param name="task">Task that must be done before Dependent task - This one is NOT updated</param>
        /// <param name="DependentTask">Task that is dependent of the other task - This one will be updated</param>
        public void AddDependencyToTask(TireServiceDB db, Task task, Task DependentTask)
        {
            // Check status of dependenttask
            if (DependentTask.TaskTypeStatusId == (int)Shared.TaskTypeStatus.ServiceDrive_Cancelled | DependentTask.TaskTypeStatusId == (int)Shared.TaskTypeStatus.ServiceDrive_Completed | DependentTask.TaskTypeStatusId == (int)Shared.TaskTypeStatus.StockTask_Cancelled | DependentTask.TaskTypeStatusId == (int)Shared.TaskTypeStatus.StockTask_Completed)
            {
                throw new ArgumentException("Den angivne afhængige opgave har en status, hvor relations angivelse ikke længere giver mening. Opgaven er enten afsluttet eller annulleret.");
            }
            else
            {
                // Add relationship/dependency from task to dependenttask - dependenttask is dependent on task is done first
                task.Tasks.Add(DependentTask);
            }   
        }

        public void CheckAndCreateTaskDependency(TireServiceDB db, int CarId, int TireSetId, int[] tasktypestatus, Task task, int TaskDetailTypeId)
        {
            List<Task> opentasks = db.GetTasksByCarTireSetAndTaskTypeStatus(CarId, TireSetId, tasktypestatus);
            if (opentasks.Count != 0)
            {
                foreach (Task opentask in opentasks)
                {
                    // Check for each taskdetail if any conflicts
                    List<TaskDetail> opentaskdetails = opentask.TaskDetails.ToList();

                    if (opentaskdetails.Count != 0)
                    {
                        foreach (TaskDetail opentaskdetail in opentaskdetails)
                        {
                            if (TaskDetailTypeId == (int)Shared.TaskDetailType.ServiceDrive_PickUpTire)
                            {
                                if (opentaskdetail.TaskDetailTypeId == (int)Shared.TaskDetailType.ServiceDrive_ChangeTire)
                                {
                                    // Add relationship/dependency from task to open task - task is dependent on open task is done first
                                    AddDependencyToTask(db, opentask, task);
                                }
                            }
                            if (TaskDetailTypeId == (int)Shared.TaskDetailType.ServiceDrive_ChangeTire)
                            {
                                if (opentaskdetail.TaskDetailTypeId == (int)Shared.TaskDetailType.ServiceDrive_PickUpTire)
                                {
                                    // Add relationship/dependency from open task to task - open task is dependt on task is done first
                                    AddDependencyToTask(db, task, opentask);
                                }
                            }
                        }
                    }
                }
            }

        }

        public void MarkTaskDetailClosed(TireServiceDB db, TaskDetail taskdetail, string NoteFromEmployee, DateTime CompletionDate, bool Completed, bool Cancelled)
        {
            taskdetail.NoteFromEmployee = NoteFromEmployee;
            taskdetail.CompletionDate = CompletionDate;
            taskdetail.Completed = Completed;
            taskdetail.Cancelled = Cancelled;
        }

        public void CreateTaskLog(TireServiceDB db, Task task, DateTime Logdate, int? FromTaskTypeStatusId, int ToTaskTypeStatusId, Guid UserId, string Note)
        {
            TaskLog tasklog = new TaskLog();

            task.TaskLogs.Add(tasklog);

            tasklog.LogDate = Logdate;
            if (FromTaskTypeStatusId != null) tasklog.FromTaskTypeStatusId = FromTaskTypeStatusId;
            tasklog.ToTaskTypeStatusId = ToTaskTypeStatusId;
            if (Note != null) tasklog.Note = Note;
            tasklog.UserId = UserId;

            db.AddTaskLog(tasklog);
        }

        public void CancelTask(TireServiceDB db, int TaskId, Guid UserId, bool SubmitInTransaction) 
        { 
            // Scenarios
            // 1. Service drive
            // 1.1. Created -> Cancel: Change status on task
            // 1.2. Planned -> Cancel: Remove planned schedule from servicecarscheduledetail
            // 1.3. Completed -> Cancel: Error, not possible
            // 1.4. Cancelled -> Cancelled: Error, not possible
            // 2. Stock task
            // 2.1. Created -> Cancel: Change status on task
            // 2.2. Completed -> Cancel: Error, not possible
            // 2.3. Cancelled -> Cancel: Error, not possible

            // Fetch task and taskdetails
            Task task = db.GetTask(Convert.ToInt32(TaskId));
            List<TaskDetail> taskdetails = task.TaskDetails.ToList();

            if (task.TaskTypeId == (int)Shared.TaskType.ServiceDrive)
            {
                // Create task log
                CreateTaskLog(db, task, DateTime.Now, task.TaskTypeStatusId, (int)Shared.TaskTypeStatus.ServiceDrive_Cancelled, UserId, "Servicekørsel annulleret");

                // Update task details
                foreach (TaskDetail taskdetail in taskdetails)
                {
                    // Update taskdetail
                    taskdetail.Completed = false;
                    taskdetail.Cancelled = true;
                }

                if (task.TaskTypeStatusId == (int)Shared.TaskTypeStatus.ServiceDrive_Created | task.TaskTypeStatusId == (int)Shared.TaskTypeStatus.ServiceDrive_Replan)
                {
                    // 1.1. Created -> Cancel: Change status on task - DONE above
                    
                }
                if (task.TaskTypeStatusId == (int)Shared.TaskTypeStatus.ServiceDrive_Planned)
                {
                    // 1.2. Planned -> Cancel: Remove planned schedule from servicecarscheduledetail
                    task.TaskTypeStatusId = (int)Shared.TaskTypeStatus.ServiceDrive_Cancelled;

                    // Fetch servicescheduledetail
                    ServiceCarScheduleDetail detail = db.GetServiceCarScheduleDetailByTaskId(task.TaskId);

                    // Clear schedule
                    // Update timeslot - reset to available status
                    ServiceCarScheduleDetailBLL detailBLL = new ServiceCarScheduleDetailBLL();
                    detailBLL.MakeServiceCarScheduleDetailAvailable(db, detail, true);

                    // Fetch car
                    Car car = db.GetCar(Convert.ToInt32(task.CarId));

                    // Check if car had planned tirechange - if yes, change status back
                    if (car.CarStatusId == (int)Shared.CarStatus.PlannedTireChange)
	                {
                        car.CarStatusId = (int)Shared.CarStatus.NeedsTireChanged;
	                }
                }
                if (task.TaskTypeStatusId == (int)Shared.TaskTypeStatus.ServiceDrive_Completed)
                {
                    // 1.3. Completed -> Cancel: Error, not possible
                    throw new ArgumentException("Opgaven er afsluttet og kan derfor ikke annulleres.");
                }
                if (task.TaskTypeStatusId == (int)Shared.TaskTypeStatus.ServiceDrive_Cancelled)
                {
                    // 1.4. Cancelled -> Cancelled: Error, not possible
                    throw new ArgumentException("Opgaven er allerede annulleret.");
                }

                // Change status on task
                task.TaskTypeStatusId = (int)Shared.TaskTypeStatus.ServiceDrive_Cancelled;
            }

            if (task.TaskTypeId == (int)Shared.TaskType.StockTask)
            {
                // Create task log
                CreateTaskLog(db, task, DateTime.Now, task.TaskTypeStatusId, (int)Shared.TaskTypeStatus.StockTask_Cancelled, UserId, "Lageropgave annulleret");

                if (task.TaskTypeStatusId == (int)Shared.TaskTypeStatus.StockTask_Created)
                {
                    // 2.1. Created -> Cancel: Change status on task - DONE above
                }
                if (task.TaskTypeStatusId == (int)Shared.TaskTypeStatus.StockTask_Completed)
                {
                    // 2.2. Completed -> Cancel: Error, not possible
                    throw new ArgumentException("Opgaven er afsluttet og kan derfor ikke annulleres.");
                }
                if (task.TaskTypeStatusId == (int)Shared.TaskTypeStatus.StockTask_Cancelled)
                {
                    // 2.3. Cancelled -> Cancel: Error, not possible
                    throw new ArgumentException("Opgaven er allerede annulleret.");
                }

                // Change status on task
                task.TaskTypeStatusId = (int)Shared.TaskTypeStatus.StockTask_Cancelled;
            }

            // Check if cancelling is being done in other transaction
            if (!SubmitInTransaction)
            {
                // Submit changes to database
                db.SubmitChanges();
            }
        }

        public void MarkServiceDriveAsCompletedByServiceCarScheduleDetailId(TireServiceDB db, int ServiceCarScheduleDetailId, bool SendEmailToCustomer, bool SendSmsToCustomer, bool AskForReview, Guid UserId, bool SubmitInTransaction)
        {
            // Fetch servicescheduledetail
            ServiceCarScheduleDetail detail = db.GetServiceCarScheduleDetail(ServiceCarScheduleDetailId);

            // Fetch task
            Task task = db.GetTask(Convert.ToInt32(detail.TaskId));

            if (task == null)
            {
                throw new ArgumentException("Opgave ikke tilknyttet service kørsel - færdigmelding kan ikke gennemføres");
            }

            MarkServiceDriveAsCompleted(db, SendEmailToCustomer, SendSmsToCustomer, AskForReview, UserId, detail, task);
            
            // Check if cancelling is being done in other transaction
            if (!SubmitInTransaction)
            {
                // Submit changes to database
                db.SubmitChanges();
            }
        }

        public void MarkServiceDriveAsCompletedByTaskId(TireServiceDB db, int TaskId, bool SendEmailToCustomer, bool SendSmsToCustomer, bool AskForReview, string CustomerFeedbackURL, Guid UserId, bool SubmitInTransaction)
        {
            // Fetch task
            Task task = db.GetTask(TaskId);

            if (task == null)
            {
                throw new ArgumentException("Opgave blev ikke fundet");
            }

            // Fetch servicescheduledetail
            ServiceCarScheduleDetail detail = db.GetServiceCarScheduleDetailByTaskId(TaskId);

            if (detail == null)
            {
                throw new ArgumentException("Opgave ikke tilknyttet service kørsel - færdigmelding kan ikke gennemføres");
            }

            MarkServiceDriveAsCompleted(db, SendEmailToCustomer, SendSmsToCustomer, AskForReview, UserId, detail, task);

            // Check if cancelling is being done in other transaction
            if (!SubmitInTransaction)
            {
                // Submit changes to database
                db.SubmitChanges();
            }
        }

        /// <summary>
        /// Function that mark a service drive as completed
        /// </summary>
        /// <param name="db">Connection to database</param>
        /// <param name="SendEmailToCustomer">True/false value to indicate if confirmation email should be send to customer</param>
        /// <param name="SendSmsToCustomer">True/false value to indicate if confirmation sms should be send to customer</param>
        /// <param name="AskForReview">True/false value if email confirmation also should contain review (trustpilot) link</param>
        /// <param name="UserId">Id on user performing this action</param>
        /// <param name="detail">ServiceDetail object task is planned on</param>
        /// <param name="task">Task that is being completed</param>
        private void MarkServiceDriveAsCompleted(TireServiceDB db, bool SendEmailToCustomer, bool SendSmsToCustomer, bool AskForReview, Guid UserId, ServiceCarScheduleDetail detail, Task task)
        {
            // Mark detail as completed
            detail.WorkCompleted = true;

            // Fetch tireset
            TireSet tireset = db.GetTireSet(Convert.ToInt32(task.TireSetId));

            // Fetch car
            Car car = db.GetCar(Convert.ToInt32(task.CarId));

            // Fetch subscription (to see if balancing is included in customer subscription)
            CustomerSubscription subscription = db.GetCustomerSubscription(Convert.ToInt32(car.CustomerSubscriptionId));
            string subscriptionnumber = car.CustomerId.ToString() + "-" + subscription.SubscriptionId.ToString() + "-" + car.CustomerSubscriptionId.ToString();

            // Fetch taskdetails
            List<TaskDetail> taskdetails = db.GetTaskDetails(task.TaskId).OrderBy(p => p.TaskDetailTypeId).ToList();

            // Place holder variable to be used in validation when taskdetailtype is return tires - to check is a tirechange was included in the task
            bool tirechange = false;
            TireSet fromtiresetplaceholder = new TireSet();

            string ServiceDone = "";

            // Loop through taskdetails and determin action
            foreach (TaskDetail taskdetail in taskdetails)
            {
                // Update taskdetail
                taskdetail.Completed = true;
                taskdetail.CompletionDate = detail.ScheduleDate;

                if (taskdetail.TaskDetailTypeId == (int)Shared.TaskDetailType.ServiceDrive_PickUpTire)
                {
                    tireset.TireSetStatusId = (int)Shared.TireSetStatus.OnStock;
                    ServiceDone += "Afhentning af hjulsæt<br>";
                    
                }
                if (taskdetail.TaskDetailTypeId == (int)Shared.TaskDetailType.ServiceDrive_ChangeTire)
                {
                    // Update car
                    car.LastTireSetChangeDate = detail.ScheduleDate;
                    car.NextTireSetChangePlanningDate = detail.ScheduleDate.AddMonths(6);
                    car.CarStatusId = (int)Shared.CarStatus.TireChanged;

                    // Update tiresets 
                    // Update from tireset
                    int[] fromtiresetid = new int[] { (int)Shared.TireSetStatus.MountedOnCar };
                    TireSet FromTireset = db.GetCarTireSetByTireSetStatus(car.CarId, fromtiresetid).FirstOrDefault();

                    // Create tiresetlog for fromtireset
                    TireSetLogBLL bll = new TireSetLogBLL();
                    bll.CreateTireSetLog(db, FromTireset, DateTime.Now, FromTireset.TireSetStatusId, (int)Shared.TireSetStatus.AwaitsMoveToStock, UserId, "Hjul afmonteret og lagt på lager", null, null, null, null, tireset.ContainerCargoRoomId);

                    // Create tiresetlog for totireset
                    bll.CreateTireSetLog(db, tireset, DateTime.Now, tireset.TireSetStatusId, (int)Shared.TireSetStatus.MountedOnCar, UserId, "Hjul påmonteret kundens bil", null, null, null, null, null);

                    // Placeholder properties for tiresetstatus and containercargoroomid
                    int containercargoroomid = Convert.ToInt32(tireset.ContainerCargoRoomId);

                    // Update totireset according to log
                    tireset.TireSetStatusId = (int)Shared.TireSetStatus.MountedOnCar;
                    tireset.ContainerCargoRoomId = null;

                    // Update fromtireset according to log
                    FromTireset.ContainerCargoRoomId = containercargoroomid;
                    FromTireset.TireSetStatusId = (int)Shared.TireSetStatus.AwaitsMoveToStock;
                    if (subscription.BalancingIncluded) FromTireset.NeedBalancing = true;

                    tirechange = true;
                    fromtiresetplaceholder = FromTireset;

                    ServiceDone += "Hjulskift<br>";
                }
                if (taskdetail.TaskDetailTypeId == (int)Shared.TaskDetailType.ServiceDrive_ReturnTire)
                {
                    if (tirechange)
                    {
                        // Tires has been changed, so update on tireset should be on other tireset
                        if (car.CarStatusId != (int)Shared.CarStatus.InActive)
                        {
                            // If car not inactive then tireset is indicated as placed at customer
                            fromtiresetplaceholder.TireSetStatusId = (int)Shared.TireSetStatus.PlacedAtCustomer;
                        }
                        else
                        {
                            // If car inactive then tireset is indicated as returned to customer
                            fromtiresetplaceholder.TireSetStatusId = (int)Shared.TireSetStatus.ReturnedToCustomer;
                        }

                        // Clear cargoroom on tireset
                        fromtiresetplaceholder.ContainerCargoRoomId = null;
                    }
                    else
                    {
                        if (car.CarStatusId != (int)Shared.CarStatus.InActive)
                        {
                            // If car not inactive then tireset is indicated as placed at customer
                            tireset.TireSetStatusId = (int)Shared.TireSetStatus.PlacedAtCustomer;
                        }
                        else
                        {
                            // If car inactive then tireset is indicated as returned to customer
                            tireset.TireSetStatusId = (int)Shared.TireSetStatus.ReturnedToCustomer;
                        }

                        // Clear cargoroom on tireset
                        tireset.ContainerCargoRoomId = null;
                    }

                    ServiceDone += "Aflevering af hjulsæt<br>";
                }
            }

            // Create task log
            CreateTaskLog(db, task, DateTime.Now, task.TaskTypeStatusId, (int)Shared.TaskTypeStatus.ServiceDrive_Completed, UserId, "Servicekørsel færdigmeldt");

            // Mark task as completed
            task.TaskTypeStatusId = (int)Shared.TaskTypeStatus.ServiceDrive_Completed;
            task.CompletionDate = detail.ScheduleDate;

            if (SendEmailToCustomer)
            {
                string CustomerFeedbackURL = "";

                if (AskForReview)
                {
                    CustomerFeedbackURL = "http://www.trustpilot.dk/evaluate/www.mobilhjulskift.dk";

                    // OUTDATE: Add trustpilot instead
                    //// Create feedback url
                    //string url = CustomerFeedbackURL + "?ServiceId=" + detail.DetailGuid.ToString() + "&CID=" + task.CustomerId.ToString();    
                }
                

                // Email customer with status on service

                // Fetch customer 
                Customer customer = db.GetCustomer(car.CustomerId);

                EmailNotificationServices emailService = new EmailNotificationServices();
                emailService.ServiceDriveCompleted_SendCustomerEmail(customer.CustomerId, customer.Email, customer.Name, subscriptionnumber, detail.ScheduleDate.ToShortDateString(), ServiceDone, AskForReview, CustomerFeedbackURL);
            }

            if (SendSmsToCustomer)
            {
                // TODO: Make code for this - use sms gateway
            }
        }
    }
}
